package hw2.nsl;

import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import java.awt.GridBagConstraints;
import javax.swing.JTextArea;
import java.awt.Rectangle;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketImpl;

import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
import java.awt.Point;

public class Mallory extends JFrame
{

    private static final long serialVersionUID = 1L;

    public static final int portno = 7001;

    private JPanel jContentPane = null;

    private JTextArea HexMsgContents_jTextArea = null;

    private JButton deleteMsg_jButton = null;

    private JButton SendToA_jButton = null;

    private JButton SendToB_jButton = null;

    private JButton Quit_jButton = null;

    private JLabel jLabel = null;

    private JTextArea Status_jTextArea = null;

    private JLabel jLabel1 = null;

    /**
     * This method initializes HexMsgContents_jTextArea
     * 
     * @return javax.swing.JTextArea
     */
    private JTextArea getHexMsgContents_jTextArea() {
        if( HexMsgContents_jTextArea == null ) {
            HexMsgContents_jTextArea = new JTextArea();
            HexMsgContents_jTextArea.setWrapStyleWord(true);
            HexMsgContents_jTextArea.setLineWrap(true);
            HexMsgContents_jTextArea.setLocation(new Point(2, 116));
            HexMsgContents_jTextArea.setSize(new Dimension(482, 110));
            HexMsgContents_jTextArea
            .setText( "Message contents will be displayed here. . ." );
            HexMsgContents_jTextArea.addKeyListener( new java.awt.event.KeyAdapter()
            {
                public void keyTyped( java.awt.event.KeyEvent e ) {
                    RawMsgContents_jTextArea.setText( hexToRaw(HexMsgContents_jTextArea.getText()) );
                }
            } );
            HexMsgContents_jTextArea
            .addCaretListener( new javax.swing.event.CaretListener()
            {
                public void caretUpdate( javax.swing.event.CaretEvent e ) {
                    RawMsgContents_jTextArea.setText( hexToRaw(HexMsgContents_jTextArea.getText()) );
                }
            } );
        }
        return HexMsgContents_jTextArea;
    }

    protected byte[] hexToByte(String text){
        text = text.substring( 2 );
        String[] chars = text.split( "\\s+\\\\x" );
        byte[] byteArray = new byte[chars.length];
        for(int i = 0; i<chars.length; i++){
            try{
            byteArray[i] = (byte) Integer.parseInt( chars[i], 16 );
            } catch (java.lang.NumberFormatException e){
                System.err.println("Could not parse: " + chars[i]);
            }
        }
        
        return byteArray;
    }
    protected String hexToRaw( String text ) {
        String result = null;
        text = text.substring( 2 );
        String[] chars = text.split( "\\s+\\\\x" );
        byte[] byteArray = new byte[chars.length];
        for(int i = 0; i<chars.length; i++){
            //unescape(chars[i]);
            //System.out.println("Converting " + chars[i]);
            //result += (byte) Integer.parseInt( chars[i], 16) + " ";
            //result += chars[i] + " ";
            byteArray[i] = (byte) Integer.parseInt( chars[i], 16 );
        }
        result = new String(byteArray);
        return result;
    }

    /**
     * This method initializes deleteMsg_jButton
     * 
     * @return javax.swing.JButton
     */
    private JButton getDeleteMsg_jButton() {
        if( deleteMsg_jButton == null ) {
            deleteMsg_jButton = new JButton();
            deleteMsg_jButton.setBounds(new Rectangle(682, 230, 126, 26));
            deleteMsg_jButton.setText( "Delete Message" );
            deleteMsg_jButton.addActionListener( new java.awt.event.ActionListener()
            {
                public void actionPerformed( java.awt.event.ActionEvent e ) {
                    HexMsgContents_jTextArea.setText( "" );
                    RawMsgContents_jTextArea.setText( "" );
                }
            } );
        }
        return deleteMsg_jButton;
    }

    /**
     * This method initializes SendToA_jButton
     * 
     * @return javax.swing.JButton
     */
    private JButton getSendToA_jButton() {
        if( SendToA_jButton == null ) {
            SendToA_jButton = new JButton();
            SendToA_jButton.setBounds(new Rectangle(2, 230, 117, 26));
            SendToA_jButton.setText("Send To Alice");
            SendToA_jButton.addActionListener( new java.awt.event.ActionListener()
            {
                public void actionPerformed( java.awt.event.ActionEvent e ) {
                    //                    NetworkMessage msg = new NetworkMessage(HexMsgContents_jTextArea.getText().getBytes(), "Bob");
                    NetworkMessage msg = new NetworkMessage(hexToByte(HexMsgContents_jTextArea.getText()), "Bob");
                    try {
                        Mallory.sendMessage( msg, "Alice" );
                    } catch( Exception e1 ) {
                        e1.printStackTrace();
                    }
                }
            } );
        }
        return SendToA_jButton;
    }

    /**
     * This method initializes SendToB_jButton
     * 
     * @return javax.swing.JButton
     */
    private JButton getSendToB_jButton() {
        if( SendToB_jButton == null ) {
            SendToB_jButton = new JButton();
            SendToB_jButton.setBounds(new Rectangle(162, 230, 117, 26));
            SendToB_jButton.setText("Send To Bob");
            SendToB_jButton.addActionListener( new java.awt.event.ActionListener()
            {
                public void actionPerformed( java.awt.event.ActionEvent e ) {
                    NetworkMessage msg = new NetworkMessage(hexToByte(HexMsgContents_jTextArea.getText()), "Alice");
                    try {
                        Mallory.sendMessage( msg, "Bob" );
                    } catch( Exception e1 ) {
                        e1.printStackTrace();
                    }
                }
            } );
        }
        return SendToB_jButton;
    }

    /**
     * This method initializes Quit_jButton
     * 
     * @return javax.swing.JButton
     */
    private JButton getQuit_jButton() {
        if( Quit_jButton == null ) {
            Quit_jButton = new JButton();
            Quit_jButton.setBounds(new Rectangle(841, 230, 126, 26));
            Quit_jButton.setText( "Quit" );
            Quit_jButton.addActionListener( new java.awt.event.ActionListener()
            {
                public void actionPerformed( java.awt.event.ActionEvent e ) {
                    // System.out.println( "actionPerformed()" ); // TODO
                    // Auto-generated Event stub actionPerformed()
                    System.exit( 0 );
                }
            } );
        }
        return Quit_jButton;
    }

    /**
     * This method initializes Status_jTextArea
     * 
     * @return javax.swing.JTextArea
     */
    private JTextArea getStatus_jTextArea() {
        if( Status_jTextArea == null ) {
            Status_jTextArea = new JTextArea();
            Status_jTextArea.setBounds(new Rectangle(2, 26, 964, 59));
            Status_jTextArea.setEditable( false );
            Status_jTextArea.setRows(5);
            Status_jTextArea
            .setText( "Message information will be displayed here. . ." );
        }
        return Status_jTextArea;
    }

    private static Mallory thisClass = null;

    private static boolean encryptedMode = true;

    private JTextArea RawMsgContents_jTextArea = null;

    /**
     * This method initializes RawMsgContents_jTextArea	
     * 	
     * @return javax.swing.JTextArea	
     */
    private JTextArea getRawMsgContents_jTextArea() {
        if( RawMsgContents_jTextArea == null ) {
            RawMsgContents_jTextArea = new JTextArea();
            RawMsgContents_jTextArea.setBounds(new Rectangle(485, 116, 482, 110));
            RawMsgContents_jTextArea.setWrapStyleWord(true);
            RawMsgContents_jTextArea.setLineWrap(true);
        }
        return RawMsgContents_jTextArea;
    }

    /**
     * @param args
     */
    public static void main( String[] args ) {
        // TODO Auto-generated method stub

        if(args.length == 1){
            if(args[0].equalsIgnoreCase( "plain" )){
                encryptedMode  = false;
            }
        }
        SwingUtilities.invokeLater( new Runnable()
        {
            public void run() {
                thisClass = new Mallory();
                thisClass.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                thisClass.setVisible( true );
            }
        } );
    }

    /**
     * This is the default constructor
     */
    public Mallory() {
        super();
        initialize();
    }

    /**
     * This method initializes this
     * 
     * @return void
     */
    private void initialize() {
        this.setSize(977, 295);
        this.setResizable(false);
        this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        this.setContentPane( getJContentPane() );
        this.setTitle( "Mallory's Interface" );
        this.addWindowListener( new java.awt.event.WindowAdapter()
        {
            public void windowOpened( java.awt.event.WindowEvent e ) {
                // System.out.println( "windowOpened()" ); // TODO Auto-generated
                // Event stub
                // windowOpened()
                new ListenerThread().start();
            }

        } );
    }

    /**
     * This method initializes jContentPane
     * 
     * @return javax.swing.JPanel
     */
    private JPanel getJContentPane() {
        if( jContentPane == null ) {
            jLabel1 = new JLabel();
            jLabel1.setBounds(new Rectangle(2, 91, 123, 24));
            jLabel1.setText( "Message Contents:" );
            jLabel = new JLabel();
            jLabel.setBounds(new Rectangle(2, 2, 147, 24));
            jLabel.setText( "Message Information:" );
            jContentPane = new JPanel();
            jContentPane.setLayout( null );
            jContentPane.setAutoscrolls( true );
            jContentPane.add( getDeleteMsg_jButton(), null );
            jContentPane.add( getSendToA_jButton(), null );
            jContentPane.add( getSendToB_jButton(), null );
            jContentPane.add( getQuit_jButton(), null );
            jContentPane.add( jLabel, null );
            jContentPane.add( getStatus_jTextArea(), null );
            jContentPane.add( jLabel1, null );
            jContentPane.add(getRawMsgContents_jTextArea(), null);
            jContentPane.add(getHexMsgContents_jTextArea(), null);
        }
        return jContentPane;
    }

    public static void sendMessage(NetworkMessage m, String destination) throws Exception{
        int port = 0;
        if(destination.equalsIgnoreCase( "alice" )){
            port = 7000;
        } else if (destination.equalsIgnoreCase("bob")){
            port = 7002;
        } else {
            System.err.println("Destination " + destination + " not recognized ");
            System.exit(1);
        }
        Socket clientSocket = new Socket("127.0.0.1",port);
        ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
        System.out.println("About to send: " + m.toHexString());
        out.writeObject(m);

        out.flush();
        out.close();
    }

    class ListenerThread extends Thread
    {
        ServerSocket sock;

        @Override
        public void run() {
            try {
                sock = new ServerSocket( Mallory.portno );
            } catch( IOException e ) {
                e.printStackTrace();
            }
            do {
                try {
                    Socket client = sock.accept();
                    ObjectInputStream in = new ObjectInputStream(
                            client.getInputStream() );
                    NetworkMessage msg = (NetworkMessage) in.readObject();
                    System.out.println("Just received: " + msg.toHexString());
                    client.close();

                    handleMessage( msg );
                } catch( Exception e ) {
                    e.printStackTrace();
                }
            } while( true );
        }

        private void handleMessage( NetworkMessage msg ) {
            String[] info = msg.parseFields();
            String source = msg.source;
            JTextArea statusTextArea = thisClass.getStatus_jTextArea();
            JTextArea hexMsgTextArea = thisClass.getHexMsgContents_jTextArea();
            JTextArea rawMsgTextArea = thisClass.getRawMsgContents_jTextArea();
            if(!encryptedMode){
                switch (info.length) {
                case 1: // step 3 of protocol
                    statusTextArea.setText( "\nStep 3 from " + source + " received.\n" );
                    //statusTextArea.setCaretPosition(statusTextArea.getText().length() - 1);
                    break;
                case 2: // step 1 of protocol
                    statusTextArea.setText( "\nStep 1 from " + source + " received.\n" );
                    //statusTextArea.setCaretPosition(statusTextArea.getText().length() - 1);
                    break;
                case 3: // step 2 of protocol
                    statusTextArea.setText( "\nStep 2 from " + source + " received.\n" );
                    //statusTextArea.setCaretPosition(statusTextArea.getText().length() - 1);
                    break;
                default: //encrypted or malformed message
                    //System.err.println( "Message structure is incorrect: " + msg );
                    statusTextArea.setText( "\nReceived message from " + source + "\n" );
                    //statusTextArea.setCaretPosition(statusTextArea.getText().length() - 1);
                }
            } else {
                statusTextArea.setText( "\nReceived message from " + source + "\n" + msg.toByteString() );
                //statusTextArea.setCaretPosition(statusTextArea.getText().length() - 1);
            }

            hexMsgTextArea.setText( msg.toHexString() );
            rawMsgTextArea.setText( hexToRaw(msg.toHexString()));

        }
    }

} // @jve:decl-index=0:visual-constraint="84,33"
